<?php
/**
 * XmuSlh, web platform of Student Association Federation of Xiamen University
 * 
 * Rewritting one or some pages of StatusNet. 
 * To get the original version, please visit <http://status.net/>.
 * 
 * StatusNet, the distributed open-source microblogging tool
 * 
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * The utility functions used everywhere in the website!
 * 
 * 通用的工具函数集.
 * 
 * @author		ytukie <ytukie@gmail.com>
 * @version		0.31
 * 
 * 0.2)  Adds the 'slh_compress' and 'slh_uncompress' functions.
 * 0.21) Adds the [user] module to handle the user login/logout operations.
 * 0.22) Adds the 'slh_redirect' function.
 * 0.23) Adds the 'slh_redirectto_default' function'.
 * 0.24) Adds the 'slh_date_string', 'slh_exact_date' function.
 * 0.25) Fixes the 'datetime' flag.
 * 		 Adds the 'slh_validate_datetimestring' function.
 * 0.26) Adds the 'slh_set_returnto', 'slh_clear_returnto' and 'slh_get_returnto' function
 * 0.27) Adds the 'slh_defaulturl' function.
 * 0.3)  Adds the getter and setter of 'usertilte' and 'userrights'.
 * 0.31) Adds the 'slh_get_userid' function.
 * 0.32) Adds the 'slh_log_deletion' function.
 */

// XmuSlh only.
if ( !defined('XMUSLH')) { exit(1); }

// Requirements.
require_once INSTALLDIR . '/lib/slh_router.php';
require_once INSTALLDIR . '/extlib/PEAR.php';
require_once INSTALLDIR . '/lib/slh_xmloutputter.php';

// XXX:[config]
/**
 * Gets the global configurations.
 */
function slh_config($main, $sub)
{
	global $config;
	return (array_key_exists($main, $config) && 
			array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
}

// XXX:[datetime]
/**
 * Return the timestamp.
 */
function slh_timestamp()
{
	return date('YmdHis');
}

function slh_sql_date($datetime)
{
	return strftime('%Y-%m-%d %H:%M:%S', $datetime);
}

function slh_sql_now()
{
	
	return strftime('%Y-%m-%d %H:%M:%S', time());
}

/**
 * Convert the 'created' column to descriptive date string.
 */
function slh_date_string($datetime)
{
    $posttime = strtotime($datetime);
    $now = time();
    $diff = $now - $posttime;

    if ($now < $posttime) { 
    	// that shouldn't happen!
        return slh_exact_date($datetime);
    } else if ($diff < 60) {
        return '几秒钟之前';
    } else if ($diff < 92) {
        return '1分钟之前';
    } else if ($diff < 3300) {
        return '几分钟之前';
    } else if ($diff < 5400) {
        return '1小时之前';
    } else if ($diff < 22 * 3600) {
       	return '几小时之前';
    } else if ($diff < 37 * 3600) {
        return '1天之前';
    } else if ($diff < 3 * 24 * 3600) {
        return '几天之前';
    } else if ($diff < 6 * 24 * 3600) {
        return '好几天之前';
    } else if ($diff < 12 * 24 * 3600) {
        return '一周之前';
    } else {
        return ($datetime);
    }
}

/**
 * Return the exact date time.
 * Too accurate and complex.
 */
function slh_exact_date($datetime)
{
    static $_utc;
    static $_siteTz;

// FIXME: The global time is different.! Fix the time zone!.
    if (!$_utc) {
        $_utc = new DateTimeZone('UTC');
        $_siteTz = new DateTimeZone(slh_config('site', 'timezone'));
    }

    $dateStr = date('d F Y H:i:s', strtotime($datetime));
    $d = new DateTime($dateStr, $_utc);
    $d->setTimezone($_siteTz);
    return $d->format(DATE_RFC850);
}

/**
 * Validate a string checking if it is a datetime string.
 * xxxx-xx-xx xx:xx:xx
 */
function slh_validate_datetimestring($str)
{
	$timestamp = strtotime($str);
	if (false == $timestamp ||
		-1 == $timestamp) {
		return false;		
	}
	
	return true;
}


// XXX:[session]
/**
 * To check if the seesion feature is availbale.
 */
function slh_have_session()
{
	return (0 != strcmp(session_id(), ''));
}

/**
 * To ensure that the session is avaliable.
 */
function slh_ensure_session()
{
	$c = null;
	if (array_key_exists(session_name(), $_COOKIE)) {
		$c = $_COOKIE[session_name()];
	}
	if ( !slh_have_session()) {
		if (array_key_exists(session_name(), $_GET)) {
			$id = $_GET[session_name()];
		} else if (array_key_exists(session_name(), $_COOKIE)) {
			$id = $_COOKIE[session_name()];
		}
		if (isset($id)) {
			session_id($id);
		}
		@session_start();
		if ( !isset($_SESSION['started'])) {
			$_SESSION['started'] = time();
			/* 
			// Deprecates the logging.
			if ( !empty($id)) {
				slh_log(LOG_WARNING, 'Session cookie "' . $_COOKIE[session_name()] . '" ' .
						' is set but started value is null');
			}
			*/
		}
	}
}

/**
 * Set the return url for some action that would be returned.
 * Like _DeleteAction.
 * 
 * @param string $returnto, the url of the former page.
 * 		  null: the current page;
 * 		  not null: a specific url.
 */
function slh_set_returnto($returnto = null)
{
	slh_ensure_session();
	if (is_null($returnto)) {
// FIXME: The total url.
		$_SESSION['returnto'] = "http://" . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
		return true;
	} else if (is_string($returnto)) {
		$_SESSION['returnto'] = $returnto;
		return true;
	} else {
		return false;
	}
}

/**
 * Clear the 'returnto' to null.
 */
function slh_clear_returnto()
{
	if (slh_have_session() && isset($_SESSION['returnto'])) {
		unset($_SESSION['returnto']);
	}
}

/**
 * Get the 'returnto' url from the SESSION.
 */
function slh_get_returnto()
{
	slh_ensure_session();
	if (slh_have_session() && isset($_SESSION['returnto'])) {
		return $_SESSION['returnto'];
	} else {
		return null;
	}
}

/**
 * Set the session token.
 */
function slh_session_token()
{
	slh_ensure_session();
	if ( !array_key_exists('token', $_SESSION)) {
		$_SESSION['token'] = slh_mtrand(32);
	}
	
	return $_SESSION['token'];
}

/**
 * returns $bytes bytes of random data as a hexadecimal string
 * "good" here is a goal and not a guarantee
 */
function slh_good_rand($bytes)
{
    if (@file_exists('/dev/urandom')) {
        return slh_urandom($bytes);
    } else {
        return slh_mtrand($bytes);
    }
}

function slh_urandom($bytes)
{
    $h = fopen('/dev/urandom', 'rb');
    // should not block
    $src = fread($h, $bytes);
    fclose($h);
    $enc = '';
    for ($i = 0; $i < $bytes; $i++) {
        $enc .= sprintf("%02x", (ord($src[$i])));
    }
    return $enc;
}

function slh_mtrand($bytes)
{
    $enc = '';
    for ($i = 0; $i < $bytes; $i++) {
        $enc .= sprintf("%02x", mt_rand(0, 255));
    }
    return $enc;
}

// XXX:[user]
// Three kinds of arguments:
// 1) a user object;
// 2) a nickname;
// 3) null to clear
// Initialize to false; set to null if none found
$_cur = false;

/**
 * Set the user infomation after login/out operation.
 */
function slh_set_user($user)
{
	global $_cur;
	
	if (is_null($user) && slh_have_session()) {
		$_cur = null;
		unset($_SESSION['userid']);
		return true;
	} else if (is_string($user)) {
		$username = $user;
		$user = User::staticGet('username', $username);
	} else if ( !($user instanceof User)) {
		return false;
	}
	
	if ($user) {
		// TODO: Some rights authorization.
		slh_ensure_session();
		$_SESSION['userid'] = $user->id;
		$_cur = $user;
		return $_cur;
	}
	
	return false;
}

/**
 * Set the title (profile.realname or association.cnname)
 */
function slh_set_usertitle($usertitle)
{
	slh_ensure_session();
	$_SESSION['usertitle'] = $usertitle;
}

/**
 * Set the rights (profile.profile_rights or association.association_rights)
 */
function slh_set_userrights($userrights)
{
	slh_ensure_session();
	$_SESSION['userrights'] = $userrights;
}

/**
 * Get the title.
 */
function slh_get_usertitle()
{
	slh_ensure_session();
	if (isset($_SESSION['usertitle']) && $_SESSION['usertitle']) {
		return $_SESSION['usertitle'];
	} else {
		return false;
	}
}

/**
 * Get the rights.
 */
function slh_get_userrights()
{
	slh_ensure_session();
	if (isset($_SESSION['userrights']) && $_SESSION['userrights']) {
		return $_SESSION['userrights'];
	} else {
		return false;
	}
}

/**
 * Who's the current user?
 */
function slh_current_user()
{
	global $_cur;
	
	if ($_cur === false) {
		if (isset($_COOKIE[session_name()]) || isset($_GET[session_name()]) ||
			(isset($_SESSION['userid']) && $_SESSION['userid'])) {
			slh_ensure_session();
			$id = isset($_SESSION['userid']) ? $_SESSION['userid'] : false;
			if ($id) {
				$user = User::staticGet($id);
				if ($user) {
					$_cur = $user;
					return $_cur;
				}
			}
		}
	}
	
	return $_cur;
}

/**
 * Get the current user_id.
 */
function slh_get_userid()
{
	if (isset($_SESSION['userid'])) {
		return $_SESSION['userid'];
	} else {
		return 0;
	}
}

/**
 * Is the current user logged in?
 */
function slh_logged_in()
{
	$user = slh_current_user();
	return ( !empty($user));
}

function slh_is_real_login()
{
	return slh_logged_in() && $_SESSION['real_login'];
}

/**
 * Logins that are 'remembered' aren't 'real' -- they're subject to
 * cookie-stealing. So, we don't let them do certain things. New reg,
 * OpenID, and password logins _are_ real.
 */
function slh_real_login($real = true)
{
	slh_ensure_session();
	$_SESSION['real_login'] = $real;
}


// XXX:[url]
/**
 * Returns the default url.
 */
function slh_defaulturl()
{
	return slh_action2url('publicnews');
}

/**
 * Gets the total URL of the given actoin.
 * exp. string => 'database' : http://xxx/xmuslh/slh_databse.php
 * 
 * Return the string URL.
 */
function slh_action2url($action, 
						$args = null, 
						$params = null, 
						$fragment = null)
{
	$router = Router::get();
	$path = $router->build($action, $args, $params, $fragment);
	
// TODO: To build a total path including servername?.
	if (mb_strpos($path, '/index.php') === 0) {
		$script = mb_substr($path, 1);
	} else {
		$script = 'index.php' . $path;
	}
	
	// Check if fancy open and need the 'index.php'
	if (slh_config('site', 'fancy')) {
		$script = mb_substr($path, 1);
	} else if (mb_strpos($path, '/index.php') === 0) {
		$script = mb_substr($path, 1);
	} else {
		$script = 'index.php' . $path;
	}
	
	$pathpart = (slh_config('site', 'path')) ? slh_config('site', 'path') . "/" : '';
	if (slh_config('site', 'server')) {
		$serverpart = slh_config('site', 'server');
	} else {
// TODO: Logs the exception.
	}
	
// TODO: Inject the session?
	return 'http://' . $serverpart . '/' . $pathpart . $script;
}

/**
 * Return the full path of the url.
 * exp: http://localhost/XmuSlh/xmuslh/js/jquery.min.js
 */
function slh_full_path($relative, 
					   $ssl = false, 
					   $addSession = true)
{
	$pathpart = (slh_config('site', 'path')) ? slh_config('site', 'path') . "/" : '';
	
	if (slh_config('site', 'server')) {
		$serverpart = slh_config('site', 'server');
	} else {
		// Logs the situation.
		// slh_log(LOG_ERR, 'Site server not configured, unable to determine site name');
	}
	
	// Inject session?
	
	return 'http://' . $serverpart . '/' . $pathpart . $relative;
}

/**
 * Redirect the page using the header.
 * Use 3xx.
 */
function slh_redirect($url, $code = 307)
{
	 static $status = array(301 => "Moved Permanently",
                            302 => "Found",
                            303 => "See Other",
                            307 => "Temporary Redirect");

    header('HTTP/1.1 ' . $code . ' ' . $status[$code]);
    header("Location: $url");

    /*
    $xo = new XMLOutputter();
    $xo->startXML('a',
                  '-//W3C//DTD XHTML 1.0 Strict//EN',
                  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');
    $xo->element('a', array('href' => $url), $url);
    $xo->endXML();
    */
    
    exit;
}

/**
 * Just redirect to the index.php page.
 */
function slh_redirectto_default()
{
// FIXME: More simplified.
	slh_redirect(slh_action2url('default'));	
}


// XXX:[log]
/**
 * To format the style of the logs.
 */
function slh_log_line($priority, $msg)
{
	static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR',
									  'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG');
	
	return date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ':' . $msg . PHP_EOL;
}

/**
 * Give the log the unique request description.
 * Simplify the content.
 */
function slh_log_request_id()
{
	$script = basename($_SERVER['PHP_SELF']);
	$method = $_SERVER['REQUEST_METHOD'];
	
	return "$script $method";
}

/**
 * Logs the current status.
 */
function slh_log($priority, $msg, $filename = null)
{
	$msg = (empty($filename)) ? $msg : basename($filename) . '-' . $msg;
	$msg = '[' . slh_log_request_id() . ']' . $msg;
	$logfile = slh_config('site', 'logfile');
	
	if ($logfile) {
		$log = fopen($logfile, "a");
		if ($log) {
			$output = slh_log_line($priority, $msg);
			fwrite($log, $output);
			fclose($log);
		}
	}
	// @Deprecates the system log mechanism.	
}

/**
 * Logs the deletion action.
 */
function slh_log_deletion($msg)
{
	$logfile = slh_config('site', 'logdeletionfile');
	
	if ($logfile) {
		$log = fopen($logfile, "a");
		if ($log) {
			$msg = '[' . $_SERVER['REQUEST_URI'] . ']' . $msg;
			$output = slh_log_line('LOG_INFO', $msg);
			fwrite($log, $output);
			fclose($log);
		}
	}
	
}

function slh_debug($msg, $filename = null)
{
	if ($filename) {
		slh_log(LOG_DEBUG, basename($filename) . ' - ' . $msg);
	} else {
		slh_log(LOG_DEBUG, $msg);
	}
}

function slh_log_db_error(&$object, $verb, $filename = null)
{
	$objstr = null;
	if (is_null($object)) {
		$objstr = 'null';
	} else if ( !($object instanceof DB_DataObject)) {
		return '(unknown)';
	} else {
		$arr = $object->toArray();
		$fields = array();
		foreach ($arr as $k => $v) {
			if (is_object($v)) {
				$fields[] = "$k='" . get_class($v) . "'";
			} else {
				$fields[] = "$k='$v'";
			}
		}
		
		$objstr = $object->tableName() . '[' . implode(',', $fields) . ']';
	}
	
	$last_error = &PEAR::getStaticProperty('DB_DataObject', 'lastError');
	if (is_object($last_error)) {
		$msg = $last_error->message;
	} else {
		$msg = 'Unknown error (' . var_export($last_error, true) . ')';
	}
	
	slh_log(LOG_ERR, $msg . '(' . $verb . ' on ' . $objstr . ')', $filename);
}

// XXX:[compress]
/**
 * Compress a string into gizp data using gzcompress, and then 
 * encode into base64 using base64_encode.
 * 
 * @return a gizp-base64 encoded string.
 */
function slh_compress($data)
{
	return base64_encode(gzcompress($data));
}

/**
 * Uncompress a gzip-base64 encoded string into normal data.
 * 
 * @param $encoded A encoded string.
 * @return A normal data string.
 */
function slh_uncompress($encoded)
{
	$base64 = base64_decode($encoded, true);
	if (empty($base64)) {
		throw new Exception('Not a base64 style code.');
	}
	return gzuncompress($base64);
}

?>